package com.lottery.game.runner.v2.utils;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.lottery.game.common.utils.DateUtils;
import com.lottery.game.common.utils.SpringApplicationUtils;
import com.lottery.game.common.utils.UUIDUtils;
import com.lottery.game.core.model.ResultEntity;
import com.lottery.game.core.modules.entity.game.GameApiLog;
import com.lottery.game.core.modules.service.app.BetService;
import com.lottery.game.core.modules.service.game.GameApiService;
import com.lottery.game.core.utils.BetUtils;
import com.lottery.game.core.utils.ResultCode;
import com.lottery.game.runner.RoomManager;

import org.springframework.util.StringUtils;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class DataUtil {

    //private static String betResultURL = "http://103.215.8.97/ingapi.php?type=";
    private static String betResultURL = "http://120.79.209.59/ingapi.php?type=";

    public static Map<String, String> betTypeMap = new HashMap<>();
    public static Map<String, String> betValueMap = new HashMap<>();

    static {
        betTypeMap.put("1", "china");
        betTypeMap.put("2", "canada");
        betTypeMap.put("4", "ssc");
        betTypeMap.put("3", "bjsc");
    }

    static {
    	betTypeMap.keySet().stream().forEach(key -> {
    		betValueMap.put(betTypeMap.get(key), key);
    	});
    }


    /**
     * 构建结果数据
     * 描述是大，小,豹子等等
     * @author zhuiyv
     * @param betService
     * @date 2018-06-17
     * @return
     */
    public static Map<String,String> getResultBet(String expect, String betTypeNumber, BetService betService) {
        Map<String,String> resultBet =  new HashMap<String,String>();
        ResultEntity resultEntity = getData(expect, betTypeNumber, betService);
        resultBet.put("status", resultEntity.getStatusCode());
        if (resultEntity.getResultInfo() != null && ResultCode.SUCCESS.equals(resultEntity.getStatusCode())) {

            List<Integer> hongboArr = Arrays.asList(1,2,7,8,12,13,18,19,23,24);
            List<Integer> lanboArr = Arrays.asList(3,4,9,10,14,15,20,25,26);
            List<Integer> lvboArr = Arrays.asList(0,5,6,11,16,17,21,22,27);

            Integer[] resultArr = (Integer[]) resultEntity.getResultInfo();
            resultBet.put("firstValue",String.valueOf(resultArr[0]));
            resultBet.put("secondValue",String.valueOf(resultArr[1]));
            resultBet.put("thirdValue",String.valueOf(resultArr[2]));
            //判断和计算
            int sum = 0;
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < resultArr.length; i++) {
                sum = sum+resultArr[i];
                //用|分割一下
                sb.append(resultArr[i]);
                sb.append("|");
            }
            sb.append(sum);
            resultBet.put("resultStr", sb.toString());
            resultBet.put("sumValue", String.valueOf(sum));
            if(sum<=13){
                //小
                resultBet.put("xiao","true");
                //极小
                if(sum<=5){
                    resultBet.put("jixiao","true");
                }
                //单双
                if(sum%2==0){
                    resultBet.put("xiaoshuang","true");
                    resultBet.put("shuang","true");
                }else{
                    resultBet.put("xiaodan","true");
                    resultBet.put("dan","true");
                }
            }else
            if(sum>=14){
                //大
                resultBet.put("da","true");
                //极大
                if(sum>=22){
                    resultBet.put("jida","true");
                }
                //单双  大单 大双
                if(sum%2==0){
                    resultBet.put("dashuang","true");
                    resultBet.put("shuang","true");
                }else{
                    resultBet.put("dadan","true");
                    resultBet.put("dan","true");
                }
            }
            //红波
            if(hongboArr.contains(sum)){
                resultBet.put("hongbo","true");
            }
            //蓝波
            if(lanboArr.contains(sum)){
                resultBet.put("lanbo","true");
            }
            //绿波
            if(lvboArr.contains(sum)){
                resultBet.put("lvbo","true");
            }
            //豹子 三个相同
            if(resultArr[0]==resultArr[1]&&resultArr[1]==resultArr[2]){
                resultBet.put("baozi","true");
            }else
                //任意两相同且不是豹子 是对子
                if(resultArr[0]==resultArr[1]||resultArr[0]==resultArr[2]||resultArr[1]==resultArr[2]){
                    resultBet.put("duizi","true");
                }

            //顺子  不需求顺序
            List<Integer> tmpList = new ArrayList<Integer>();
            tmpList.addAll(Arrays.asList(resultArr));
            tmpList.sort(new Comparator<Integer>() {
                @Override
                public int compare(Integer o1, Integer o2) {
                    return  o1-o2;
                }

            });
            //排序后结果递增1是顺子
            if(tmpList.get(0)+1==tmpList.get(1)&&tmpList.get(1)+1==tmpList.get(2)) {
                resultBet.put("shunzi","true");
            }

            //排序后结果如果是  089   019 也是顺子
            if(tmpList.get(0)==0&&(tmpList.get(1)==1||tmpList.get(1)==8)&&tmpList.get(2)==9) {
                resultBet.put("shunzi","true");
            }
        }

        return resultBet;
    }

    /**
     * 基本获取数据
     * @author zhuiyv
     * @date 2018-06-17
     * @return
     */
    public static ResultEntity getData(String expect, String betTypeNumber, BetService betService) {

        GameApiService gameApiService = SpringApplicationUtils.getBean(GameApiService.class);
        String[] urls = gameApiService.findByTicket(Integer.parseInt(betTypeNumber));
        //获取加拿大28数据
        ResultEntity re = new ResultEntity();
        re.setStatusCode(ResultCode.ERROR);
        JSONArray jsonArr = null;
        try {
            int time = 0;
            JSONObject resultObj = null;
            while (ResultCode.ERROR.equals(re.getStatusCode()) && time < 180) {
                time ++;
                log.info("彩种类型【{}】,准备开奖期次【{}】,尝试第【{}】次开奖，当前时间【{}】", betTypeNumber, expect, time, DateUtils.getNowDateString());
                try {
                    jsonArr = GameResultGetUtils.get(urls);
                } catch (Exception e) {
                    log.error(e.getMessage(), e);
                }

                if (jsonArr == null) {
                    re.setStatusCode(ResultCode.ERROR);
                    re.setResultInfo("获取到数据为空");
                } else {

                    for (int i = 0; i < jsonArr.size(); i++) {
                        String tmpExpect = jsonArr.getJSONObject(i).getString("number");
                        if (expect.equals(tmpExpect)) {
                            resultObj = jsonArr.getJSONObject(i);
                            re.setStatusCode(ResultCode.SUCCESS);
                            break;
                        }
                    }
                    if (resultObj == null) {
                        re.setStatusCode(ResultCode.ERROR);
                        re.setResultInfo("获取数据异常，无法获取" + expect + "期数据");
                    }
                }

                //没有获取到，等3秒后重新获取
                if (ResultCode.ERROR.equals(re.getStatusCode())) {
                    try {
                        Thread.sleep(1000L);
                    } catch (InterruptedException e) {
                        log.error(e.getMessage(), e);
                    }
                } else {
                    break;
                }
            }

            if (resultObj != null) {

                Integer one = Integer.parseInt(resultObj.getString("one"));
                Integer two = Integer.parseInt(resultObj.getString("two"));
                Integer three = Integer.parseInt(resultObj.getString("three"));
                re.setStatusCode(ResultCode.SUCCESS);
                re.setResultInfo(new Integer[]{one,two,three});
                log.info("获取开奖结果成功 期号【{}】,开奖结果:【{},{},{}】", expect, one, two, three);

                //记录日志
                GameApiLog apiLog = getApiLogByJson(resultObj);
                apiLog.setType(betTypeNumber);
                apiLog.setStatus("1");
                betService.insertApiLog(apiLog);
            }
        } catch (Exception e) {
            e.printStackTrace();
            re.setStatusCode(ResultCode.ERROR);
            re.setResultInfo("获取数据异常，"+e.getMessage());
        }
        return re;
    }

    /**
     * 获取下期期号和开奖时间
     * @param roomType
     * @return
     */
    public static ResultEntity getIssueApi(RoomManager.RoomBean roomType) {

        GameApiService gameApiService = SpringApplicationUtils.getBean(GameApiService.class);
        String[] urls = gameApiService.findByTicket(Integer.parseInt(roomType.getBetType()));
        //获取加拿大28数据
        ResultEntity re = new ResultEntity();
        re.setStatusCode(ResultCode.ERROR);
        JSONArray ja =null;
        try {
            int time = 0;
            while(ResultCode.ERROR.equals(re.getStatusCode()) && time<2) {
                time++;
                try {
                    //主要获取 第一次主要，第二次备用
                    ja = GameResultGetUtils.get(urls);
                } catch (Exception e) {
                    log.error(e.getMessage(), e);
                }

                if (ja == null) {
                    re.setStatusCode(ResultCode.ERROR);
                    re.setResultInfo("获取到数据为空");
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    re.setStatusCode(ResultCode.SUCCESS);
                }
            }
            if (ja == null) {
                return re;
            }

            //如果是
            if(StringUtils.isEmpty(roomType.getExpect())) {
                //最近一组数据
                JSONObject first =(JSONObject) ja.get(0);
                String  lastExpect = first.getString("number");
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                sdf.setTimeZone(TimeZone.getTimeZone("GMT+8"));
                Date lastOpenTime = sdf.parse(first.getString("time"));

                //记录日志
                if(roomType.getBetService()!=null) {
                    GameApiLog apiLog = getApiLogByJson(first);
                    apiLog.setType(roomType.getBetType());
                    apiLog.setStatus("0");
                    roomType.getBetService().insertApiLog(apiLog);
                }

                //获取最新一期期号，
                Map<String,Object> map = new HashMap<String, Object>();
                map.put("expect", lastExpect);
                map.put("sleepTime", 0);
                map.put("openTime", lastOpenTime);
                re.setStatusCode(ResultCode.SUCCESS);
                re.setResultInfo(map);

            } else {

                String expect = roomType.getExpect();

                //获取expect 对应的开奖时间
                ResultEntity getOpenTimeResult = getNextExpectOpenDate(roomType);
                if(!ResultCode.SUCCESS.equals(getOpenTimeResult.getStatusCode())) {
                    re.setStatusCode(ResultCode.ERROR);
                    re.setResultInfo("获取开奖时间异常,返回：" + getOpenTimeResult.getResultInfo().toString());
                    return re;
                }
                Date nextExpectOpenTime = (Date) getOpenTimeResult.getResultInfo();

                //期号还是当期，开奖时间为下期
                Map<String,Object> map = new HashMap<String, Object>();
                map.put("expect", expect);
                map.put("sleepTime", 0);
                map.put("openTime", nextExpectOpenTime);
                re.setStatusCode(ResultCode.SUCCESS);
                re.setResultInfo(map);
            }
        } catch (Exception e) {
            e.printStackTrace();
            re.setStatusCode(ResultCode.ERROR);
            re.setResultInfo("获取数据异常，"+e.getMessage());
        }
        return re;
    }

    public static JSONArray httpRequest(String requestUrl, String requestMethod) {
        JSONArray jsonArr;
        StringBuffer buffer = new StringBuffer();
        try {
            URL url = new URL(requestUrl);
            // http协议传输
            HttpURLConnection httpUrlConn = (HttpURLConnection) url.openConnection();
            httpUrlConn.setDoOutput(true);
            httpUrlConn.setDoInput(true);
            httpUrlConn.setUseCaches(false);
            // 设置请求方式（GET/POST）
            httpUrlConn.setRequestMethod(requestMethod);

            // if ("GET".equalsIgnoreCase(requestMethod))
            httpUrlConn.connect();
            // 将返回的输入流转换成字符串
            InputStream inputStream = httpUrlConn.getInputStream();
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

            String str = null;
            while ((str = bufferedReader.readLine()) != null) {
                buffer.append(str);
            }
            bufferedReader.close();
            inputStreamReader.close();
            // 释放资源
            inputStream.close();
            inputStream = null;
            httpUrlConn.disconnect();
            jsonArr = JSONArray.parseArray(buffer.toString());
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            e.printStackTrace();
            return null;
        }
        log.info("请求地址[{}], 请求方法[{}], 返回数据[{}]", requestUrl, requestMethod, jsonArr.toJSONString());
        return jsonArr;
    }

    /**
     * 获取下期开奖时间
     * @param roomType
     * @return
     * @throws ParseException
     */
    public static ResultEntity getNextExpectOpenDate(RoomManager.RoomBean roomType)  {
        // testChangeURL();
        //返回数据
        ResultEntity re = new ResultEntity();
        re.setStatusCode(ResultCode.ERROR);
        //游戏期号
        String expect = roomType.getExpect();

        GameApiService gameApiService = SpringApplicationUtils.getBean(GameApiService.class);
        String[] urls = gameApiService.findByTicket(Integer.parseInt(roomType.getBetType()));

        JSONArray ja = null;
        try {
            int time = 0;
            while (ResultCode.ERROR.equals(re.getStatusCode()) && time < 3) {
                time ++;
                try {
                    ja = GameResultGetUtils.get(urls);
                } catch (Exception e) {
                    // e.printStackTrace();
                }
                if(ja==null) {
                    re.setStatusCode(ResultCode.ERROR);
                    re.setResultInfo("获取到数据为空");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }else {
                    re.setStatusCode(ResultCode.SUCCESS);
                }
            }

            if (ja == null) {
                return re;
            }
        }catch (Exception e) {
            e.printStackTrace();
            re.setStatusCode(ResultCode.ERROR);
            re.setResultInfo("api异常："+e.getMessage());
            return re;
        }


        //获取开奖结果list有部分不可用，去除掉
        List<JSONObject> objList = new ArrayList<>();
        int flagNum =0;
        for (int i = 0; i < ja.size(); i++) {
            JSONObject objTmp = ja.getJSONObject(i);
            if(checkJSONObject(objTmp, roomType.getGameTime())) {
                objList.add(objTmp);
                flagNum = flagNum+1;
            }else {
                objList.add(null);
            }

            //尝试记录第一期，所有都操作的话会有大量重复操作
            if(i==0) {
                //记录日志
                GameApiLog apiLog = getApiLogByJson(objTmp);
                apiLog.setType(roomType.getBetType());
                apiLog.setStatus("1");
                roomType.getBetService().insertApiLog(apiLog);
            }
        }
        //如果最后一期都不能用，需要使用缓存开奖结果
        if(flagNum==0||objList.get(0)==null) {
            re.setResultInfo("无法获取结果，最新一期都不可用！");
            re.setStatusCode(ResultCode.ERROR);
            return re;
        }

        //开奖时间list
        List<Date> openTimeList = new ArrayList<>();
        for (int i = 0; i < objList.size(); i++) {
            JSONObject objTmp = objList.get(i);
            if(objTmp!=null) {
                Date thisOpenTime = getObjOpenTime(objTmp,expect,roomType.getGameTime());
                if(thisOpenTime!=null) {
                    openTimeList.add(thisOpenTime);
                }
            }
        }

        if(openTimeList.size()>0) {
            long sumTimeNum = 0;
            for (int i = 0; i < openTimeList.size(); i++) {
                sumTimeNum = sumTimeNum +  openTimeList.get(i).getTime();
            }

            long avgTimeNum = sumTimeNum/openTimeList.size();

            List<Date> tmpDateList = new ArrayList<>();
            tmpDateList.addAll(openTimeList);
            //晚于平均值的都去掉
            if (tmpDateList.size() >= 2) {
                for (int i = 0; i < tmpDateList.size(); i++) {
                    if((tmpDateList.get(i).getTime() - avgTimeNum) >= 0) {
                        openTimeList.remove(tmpDateList.get(i));
                    }
                }
                //再平均一次
                sumTimeNum = 0;
                for (int i = 0; i < openTimeList.size(); i++) {
                    sumTimeNum = sumTimeNum +  openTimeList.get(i).getTime();
                }
                avgTimeNum = sumTimeNum/openTimeList.size();
            }
            Date openTime = new Date(avgTimeNum);
            re.setResultInfo(openTime);
            re.setStatusCode(ResultCode.SUCCESS);
        }
        return re;
    }

    /**
     * 根据objTmp里的time时间计算 expect期的时间
     * 比如 objTmp是 001期 2018-08-19 15:30:00   获取expect为005期的数据时，为objTmp的时间加上 （5-1）*gameTime
     * @param objTmp
     * @return
     */
    private static Date getObjOpenTime(JSONObject objTmp,String expect,int gameTime){
        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            sdf.setTimeZone(TimeZone.getTimeZone("GMT+8"));
            String opentimeStr = objTmp.getString("time");
            Date openTime = sdf.parse(opentimeStr);
            //获取期号差异
            String objExpect = objTmp.getString("number");
            int expectDiffInt = BetUtils.getExpectNum(expect) - BetUtils.getExpectNum(objExpect);
            openTime = BetUtils.addSeconds(openTime, expectDiffInt*gameTime);
            return openTime;
        } catch (Exception e) {
            log.error("111objTmp的time时间无法转换！"+e.getMessage());
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 检查JSONOBject是否是有效数据
     * 比如19:00维护到20:00   20:05 获取到的前几期有19:00之前开的，需要把它们排除掉，   如果距离当前时间超出8个游戏单位时间，则认为是无效数据
     * @param objTmp 需要校验的数据
     * @param gameTime 游戏时间
     *  提前量，要校验的期，是当前的前一期 还是前两期 还是前三期
     * @throws ParseException
     */
    private static boolean checkJSONObject(JSONObject objTmp, int gameTime)  {
        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            sdf.setTimeZone(TimeZone.getTimeZone("GMT+8"));
            String opentimeStr = objTmp.getString("time");
            Date openTime = sdf.parse(opentimeStr);
            long timdif = ((new Date()).getTime()-openTime.getTime())/1000;
            if(timdif > gameTime*8) {
                return false;
            }else {
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }


    private static GameApiLog getApiLogByJson(JSONObject objTmp){
        GameApiLog apiLog = new GameApiLog();
        apiLog.setId(UUIDUtils.randomUUID());
        apiLog.setIssueApi(objTmp.getString("number"));
        apiLog.setOpenTime(objTmp.getString("time"));
        apiLog.setResult(objTmp.toJSONString());
        return apiLog;
    }

    //测试方法
//	private static void testChangeURL()  {
//	    String beginTime = "00:06";
//	    String endTime ="00:40";
//	    Date now = new Date();
//	    SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");
//	    String nowStr = sdf.format(now);
//	    try {
//            if(sdf.parse(nowStr).getTime()>=sdf.parse(beginTime).getTime()&&sdf.parse(nowStr).getTime()<sdf.parse(endTime).getTime()) {
//                betResultURL = "http://wronghost/ingapi.php?type=";
//            }else {
//                betResultURL = "http://103.215.8.97/ingapi.php?type=";
//            }
//        } catch (ParseException e) {
//            // TODO Auto-generated catch block
//            e.printStackTrace();
//        }
//	}

}
